package com.tcm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tcm.common.enums.CachePrefix;
import com.tcm.entity.*;
import com.tcm.entity.bo.UserCacheBo;
import com.tcm.entity.dto.param.ClazzPageParam;
import com.tcm.entity.dto.ClazzUpdateDto;
import com.tcm.entity.vo.ClazzListResult;
import com.tcm.entity.vo.ClazzSelectResult;
import com.tcm.entity.resp.PageR;
import com.tcm.exception.DatabaseOperationException;
import com.tcm.exception.EntityInvalidException;
import com.tcm.mapper.ClazzMapper;
import com.tcm.service.*;
import com.tcm.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 班级业务实现类
 * @author Guqier
 * @version 1.0
 * @date 2023/7/2 17:25
 */
@Slf4j
@Service
public class ClazzServiceImpl extends ServiceImpl<ClazzMapper, Clazz> implements ClazzService {


    @Autowired
    private OrganizationService orgService;

    @Autowired
    private CodeService codeService;

    @Autowired
    private StudentService studentService;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private TeachingService teachingService;

    @Autowired
    private ClazzService clazzService;

    @Autowired
    private TeacherService teacherService;


    /**
     * 添加班级信息
     * @param clazzInfo 班级更新信息封装对象
     * @param currUserId 当前登录用户ID
     * @return 添加结果，true为成功
     * @throws EntityInvalidException 学院、专业或年级不存在
     */
    @Override
    @Transactional
    public boolean addClazzInfo(ClazzUpdateDto clazzInfo, Long currUserId) throws EntityInvalidException{
        // 给数据对象封装
        Clazz clazz = new Clazz();
        BeanUtils.copyProperties(clazzInfo, clazz);

        // 数据校验
        Organization college = orgService.getById(clazz.getCollegeId());
        Organization major = orgService.getById(clazz.getMajorId());
        Code grade = codeService.getById(clazz.getGradeId());
        if (college == null || major == null || grade == null) {
            throw new EntityInvalidException("学院、专业或年级不存在");
        }

        // 完善数据
        // TODO 权限码
        clazz.setCreateUser(currUserId);
        clazz.setUpdateUser(currUserId);

        return this.save(clazz);
    }

    /**
     * 分页获取班级列表
     * @param clazzPageParam 获取班级分页数据参数封装类
     * @return 班级列表
     */
    @Override
    public PageR<ClazzListResult> getClazzListByPage(ClazzPageParam clazzPageParam){
        PageR<ClazzListResult> res = new PageR<>();
        // 根据参数构造条件构造器
        LambdaQueryWrapper<Clazz> queryWrapper = new LambdaQueryWrapper<>();
        Page<Clazz> page = new Page<>(clazzPageParam.getPage(), clazzPageParam.getPageSize());
        page.addOrder(OrderItem.desc("update_time"));

        queryWrapper.eq(clazzPageParam.getCollegeId() != null, Clazz::getCollegeId, clazzPageParam.getCollegeId());// 如果指定了学院ID
        queryWrapper.eq(clazzPageParam.getMajorId() != null, Clazz::getMajorId, clazzPageParam.getMajorId());// 如果指定了专业ID
        queryWrapper.eq(clazzPageParam.getGradeId() != null, Clazz::getGradeId, clazzPageParam.getGradeId());// 如果指定了年级ID

        this.page(page, queryWrapper);// 分页查询结果将封装在page中
        BeanUtils.copyProperties(page, res, "records");// 将数据封装到结果集

        // 将Clazz数据封装到ClazzListResult中，便于展示数据
        List<ClazzListResult> list = new ArrayList<>();
        List<Clazz> records = page.getRecords();
        for (Clazz record : records) {
            ClazzListResult clazzListResult = new ClazzListResult();
            clazzListResult.setId(record.getId());
            clazzListResult.setName(record.getName());
            // 封装学院名称
            clazzListResult.setCollegeId(record.getCollegeId());
            Organization college = orgService.getById(record.getCollegeId());
            clazzListResult.setCollegeName(college.getName());
            // 封装专业名称
            clazzListResult.setMajorId(record.getMajorId());
            Organization major = orgService.getById(record.getMajorId());
            clazzListResult.setMajorName(major.getName());
            // 封装年级名称
            clazzListResult.setGradeId(record.getGradeId());
            Code grade = codeService.getById(record.getGradeId());
            clazzListResult.setGradeName(grade.getName());
            // 封装班级人数
            LambdaQueryWrapper<Student> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(Student::getClazzId, record.getId());
            int count = studentService.count(lambdaQueryWrapper);
            clazzListResult.setNum(count);
            list.add(clazzListResult);
        }
        res.setRecords(list);
        return res;
    }

    /**
     * 修改班级信息
     * @param clazzUpdateDto 班级信息更新数据对象
     * @param currUserID 当前登录用户ID
     * @return 处理结果，true为成功
     * @throws EntityInvalidException 实体不存在异常
     */
    @Override
    @Transactional
    public boolean modifyClazzInfo(ClazzUpdateDto clazzUpdateDto, Long currUserID) throws EntityInvalidException{
        Clazz clazz = this.getById(clazzUpdateDto.getId());
        // 判断班级是否存在
        if (clazz == null) {
            throw new EntityInvalidException("班级不存在");
        }

        // 数据校验
        Organization college = orgService.getById(clazzUpdateDto.getCollegeId());
        Organization major = orgService.getById(clazzUpdateDto.getMajorId());
        Code grade = codeService.getById(clazzUpdateDto.getGradeId());
        if (college == null || major == null || grade == null) {
            throw new EntityInvalidException("学院、专业或年级不存在");
        }

        // 保存数据
        BeanUtils.copyProperties(clazzUpdateDto, clazz);
        clazz.setUpdateUser(currUserID);
        return this.updateById(clazz);
    }

    /**
     * 删除班级信息
     * @param ids id列表
     * @param currUserId 当前登录用户ID
     * @return 删除结果，true为成功
     * @throws DatabaseOperationException 数据库操作失败异常
     */
    @Override
    @Transactional
    public boolean removeClazzInfo(List<String> ids, Long currUserId) throws DatabaseOperationException{
        int cnt = 0;
        for (String id : ids) {
            Clazz clazz = this.getById(id);
            if (clazz == null) {
                continue;// 不存在
            }
            clazz.setUpdateUser(currUserId);
            this.updateById(clazz);
            boolean res = this.removeById(id);
            if (res){
                cnt++;
            }
        }
        if (cnt != ids.size()){
            throw new DatabaseOperationException("数据库操作异常，删除失败");
        }
        return true;
    }

    /**
     * 通过当前登录教师用户获取所教班级列表
     * @param teacherUserId 当前登录教师用户
     * @return 所教班级列表
     */
    @Override
    public List<ClazzSelectResult> getClazzListAccordingToTeacher(Long teacherUserId){
        // 从缓存中获取当前登录用户信息
        UserCacheBo userCacheBo = redisUtil.getCacheObject(CachePrefix.PC_USER_TOKEN.getCacheKey(String.valueOf(teacherUserId)));
        Teacher teacher = teacherService.getTeacherByAccount(userCacheBo.getAccount());
        if (teacher == null) {
            return null;// 如果当前登录用户是管理员而不是教师
        }
        // 查询指定教师的教学计划
        LambdaQueryWrapper<Teaching> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Teaching::getTeacherId, teacher.getId());
        List<Teaching> teachings = teachingService.list(queryWrapper);
        // 从教学计划中提取班级
        List<Clazz> clazzList = clazzService.list();
        return clazzList.stream().filter(clazz -> teachings.stream().map(Teaching::getClazzId).distinct().collect(Collectors.toList()).contains(clazz.getId())).map(clazz -> {
            ClazzSelectResult clazzSelectResult = new ClazzSelectResult();
            clazzSelectResult.setId(clazz.getId());
            clazzSelectResult.setName(clazz.getName());
            return clazzSelectResult;
        }).collect(Collectors.toList());
    }

}
